package insight.springia5.chapter06.api.controller;

import insight.springia5.chapter06.api.hateoas.TacoResource;
import insight.springia5.chapter06.api.hateoas.TacoResourceAssembler;
import insight.springia5.chapter06.data.TacoRepository;
import insight.springia5.chapter06.domain.Taco;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.hateoas.CollectionModel;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Optional;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.linkTo;
import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.methodOn;

/**
 * 使用 @RestController 将返回值写入 HTTP 请求的响应体中
 * 分解： @RestController = @Controller + @ResponseBody
 * <p>
 * 在 @RequestMapping 中指定 produces 属性为 application/json
 * 1. 指明只处理请求的头信息中 Accept 字段为 application/json 的请求
 * 2. 限制返回值为 JSON
 * 3. 允许其他 Controller 处理相同路径 但不为 JSON 格式的请求
 * <p>
 * 使用 @CrossOrigin 解决跨域问题 (类似 Gateway)
 * 可作用在类和方法上 默认允许任何域请求
 *
 * @date 2021/7/19
 * 添加超媒体：将 api 与客户端的 url 解耦, 减少硬编码
 *
 * @author insight
 * @since 2021/7/17
 */
@Api(tags = "设计 Taco")
@RestController
@RequestMapping(path = "/design", produces = "application/json")
@CrossOrigin
public class DesignController {
    private TacoRepository tacoRepository;

    @Autowired
    public DesignController(TacoRepository tacoRepository) {
        this.tacoRepository = tacoRepository;
    }


    @ApiOperation("查看最近设计的 Taco")
    @GetMapping("/recent")
    public Iterable<Taco> recentTacos() {
        PageRequest page = PageRequest.of(0, 12,
                Sort.by("createdAt").descending());
        return tacoRepository.findAll(page).getContent();
    }

    /**
     * 使用超链接 返回资源类型
     *
     * 同时再抱怨一下 SpringiA5 还是太老了... 好多类的名字都变了
     * 官方更新日志：https://docs.spring.io/spring-hateoas/docs/current/reference/html/#migrate-to-1.0.changes.representation-models
     *
     * 顺带一提 hateoas 是 restful 的 level3 规范
     * 但也只是规范而已 在国内还是没人用的
     */
    @GetMapping("/recenth")
    public CollectionModel<TacoResource> recentTacosH() {
        PageRequest page = PageRequest.of(0, 12,
                Sort.by("createdAt").descending());
        List<Taco> tacos = tacoRepository.findAll(page).getContent();
        /* 转换为携带资源链接的集合
        CollectionModel<EntityModel<Taco>> recentTaco =
                CollectionModel.wrap(tacos);*/

        // 使用工具类获取 CollectionModel
        CollectionModel<TacoResource> recentTaco =
                new TacoResourceAssembler().toCollectionModel(tacos);

        /* 添加硬编码链接
        recentTaco.add(
                Link.of("http://localhost:8080/design/recent", "recents")
        );*/
        /*
          取消部分硬编码
          使用 WebMvcLinkBuilder 请求一个链接
          slash 斜杠
        recentTaco.add(
                WebMvcLinkBuilder.linkTo(DesignController.class)
                        .slash("recenth")
                        .withRel("recents")
        );
        */

        // 连 slash 都不需要 取消所有硬编码
        // 将控制器一个方法传递给 linkTo()
        recentTaco.add(
                linkTo(methodOn(DesignController.class).recentTacosH())
                        .withRel("recents")
        );

        return recentTaco;
    }

    @ApiOperation("根据 id 获取 Taco")
    @GetMapping("/{id}")
    public ResponseEntity<Taco> getTacoById(@PathVariable("id") long id) {
        Optional<Taco> taco = tacoRepository.findById(id);

        // 返回 ResponseEntity<Taco> 类似自定义结果集
        return taco.map(value -> new ResponseEntity<>(value, HttpStatus.OK)).orElseGet(() -> new ResponseEntity<>(null, HttpStatus.NOT_FOUND));
    }
    /*public Taco getTacoById(@PathVariable("id") long id) {
        Optional<Taco> taco = tacoRepository.findById(id);
        // 如果是 null 应当返回 404
        return taco.orElse(null);
    }*/

    /**
     * 提交一个设计好的 Taco
     * <p>
     * 1. consumes 用于输入/提交 作用于 Content-Type
     * 2. produces 用于输出/返回 作用于 Accept
     * 手动指定返回状态码 描述性更强更具体
     * 使用 @RequestBody 表明把 JSON 请求转换成一个对象 绑定到方法参数
     */
    @ApiOperation("提交一个设计好的Taco")
    @PostMapping(consumes = "application/json")
    @ResponseStatus(HttpStatus.CREATED)
    public Taco postTaco(@RequestBody Taco taco) {
        return tacoRepository.save(taco);
    }
}